package com.example.network;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import android.os.AsyncTask;

public class DmScan extends AsyncTask<Void, Integer, Void>
{
	private final static int[] DPORTS = { 139, 445, 22, 80 };
    private final static int TIMEOUT_SCAN = 3600; // seconds
    private final static int TIMEOUT_SHUTDOWN = 10; // seconds
    private final static int THREADS = 10;
    
    private int pt_move = 2;
	protected int hosts_done = 0;
	
	protected long ip;
    protected long start = 0;
    protected long end = 0;
    protected long size = 0;
    
    private ExecutorService mPool;
	
	public String getLocalIpAddress()
    {
		try
		{
			for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); )
			{
				NetworkInterface intf = en.nextElement();
				for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements(); )
				{
					InetAddress inetAddress = enumIpAddr.nextElement();
					if (!inetAddress.isLoopbackAddress())
					{
						return inetAddress.getHostAddress().toString();
					}
				}
			}
		}
		catch (SocketException ex)
		{
			//
		} 
		return null;
	}
	
	public String scanDmMachine()
	{
		String strIpAddr = getLocalIpAddress();
		
		ip = NetInfo.getUnsignedLongFromIp(strIpAddr);
		
		int shift = (32 - 24);
		
        start = (ip >> shift << shift) + 1;
        end = (start | ((1 << shift) - 1)) - 1;
        
        execute();
        
        return "192.168.1.168";
	}
	
	@Override
	protected Void doInBackground(Void... params)
	{
		mPool = Executors.newFixedThreadPool(THREADS);
		
        if (ip <= end && ip >= start)
        {
            // gateway
            launch(start);

            // hosts
            long pt_backward = ip;
            long pt_forward = ip + 1;
            long size_hosts = size - 1;

            for (int i = 0; i < size_hosts; i++) {
                // Set pointer if of limits
                if (pt_backward <= start) {
                    pt_move = 2;
                } else if (pt_forward > end) {
                    pt_move = 1;
                }
                // Move back and forth
                if (pt_move == 1) {
                    launch(pt_backward);
                    pt_backward--;
                    pt_move = 2;
                } else if (pt_move == 2) {
                    launch(pt_forward);
                    pt_forward++;
                    pt_move = 1;
                }
            }
        } else {
            for (long i = start; i <= end; i++) {
                launch(i);
            }
        }
        mPool.shutdown();
        try
        {
            if (!mPool.awaitTermination(TIMEOUT_SCAN, TimeUnit.SECONDS))
            {
                mPool.shutdownNow();
                if (!mPool.awaitTermination(TIMEOUT_SHUTDOWN, TimeUnit.SECONDS))
                {
                }
            }
        }
        catch (InterruptedException e)
        {
            mPool.shutdownNow();
            Thread.currentThread().interrupt();
        }
        finally
        {
        }
        
		return null;
	}
	
	private void launch(long i) {
        if(!mPool.isShutdown()) {
            mPool.execute(new CheckRunnable(NetInfo.getIpFromLongUnsigned(i)));
        }
    }
	
	@Override
    protected void onPreExecute()
	{
		size = (int) (end - start + 1);
    }
	
	@Override
    protected void onProgressUpdate(Integer... Void)
	{
    }
	
	@Override
    protected void onPostExecute(Void unused)
	{
    }
	
	@Override
    protected void onCancelled()
	{
        super.onCancelled();
    }
	
	private class CheckRunnable implements Runnable
	{
        private String addr;

        CheckRunnable(String addr)
        {
            this.addr = addr;
        }

        public void run()
        {
            if (isCancelled())
            {
                publish(0);
            }
            
            try
            {
            	int host = 0;
                InetAddress h = InetAddress.getByName(addr);
                
                // Arp Check #1
                String hardwareAddress = HardwareAddress.getHardwareAddress(addr);
                if (!NetInfo.NOMAC.equals(hardwareAddress))
                {
                    publish(host);
                    return;
                }
                // Arp Check #2
                hardwareAddress = HardwareAddress.getHardwareAddress(addr);
                if(!NetInfo.NOMAC.equals(hardwareAddress))
                {
                    publish(host);
                    return;
                }
                // Arp Check #3
                hardwareAddress = HardwareAddress.getHardwareAddress(addr);
                if (!NetInfo.NOMAC.equals(hardwareAddress))
                {
                    publish(host);
                    return;
                }
                publish(0);

            }
            catch (IOException e)
            {
                publish(0);
            } 
        }
    }
	
	private void publish(final int host)
	{
        hosts_done++;
        
        if (host == 0)
        {
            publishProgress(0);
            return; 
        }

        publishProgress(0);
    }
}